﻿using ENet;
using huqiang.Collections;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace huqiang.Communication
{
    public class ENetClient
    {
        public uint ip;
        public int port;
        public int buffIndex;
        public Int64 id;
        public IPEndPoint endPoint;
        int MsgID = 0;

        internal Peer peer;
        internal byte channelID;
        internal bool _connect;
        /// <summary>
        /// 第一次连接时间
        /// </summary>
        public long ConnectTime;
        internal LockQueueS<NetworkContent> ncStack = new LockQueueS<NetworkContent>(64);
        public virtual void Awake()
        {
        }
        /// <summary>
        /// 注意此函数为分线程调用，UI线程请使用线程任务委托
        /// </summary>
        public virtual void Connect()
        {
        }
        public virtual void Disconnect()
        {
        }
        internal void SafeSend()
        {
            int nc = ncStack.Count;
            for (int i = 0; i < nc; i++)
            {
                var msg = ncStack.Dequeue();
                SendUnreliable(msg.dat);
            }
        }
        private void SendReliable(byte[] data)
        {
            Packet packet = default(Packet);

            packet.Create(data, data.Length, PacketFlags.Reliable | PacketFlags.NoAllocate); // Reliable Sequenced
            peer.Send(channelID, ref packet);
        }
        private void SendUnreliable(byte[] data)
        {
            Packet packet = default(Packet);

            packet.Create(data, data.Length, PacketFlags.None | PacketFlags.NoAllocate); // Unreliable Sequenced
            peer.Send(channelID, ref packet);
        }
        public virtual void Dispatch(IntPtr ptr, int len)
        {
            try
            {
                UserMsg msg;
                string str = null;
                byte[] buf = null;
                unsafe
                {
                    msg = *(UserMsg*)ptr;
                    len = len - UserMsg.Size;
                    if (msg.AllPart > 128)
                        return;
                    if (msg.AllPart == 1)
                    {
                        if (msg.DataType == EnvelopeType.String | msg.DataType == EnvelopeType.Json)
                        {
                            if (len > 0)
                            {
                                str = Encoding.UTF8.GetString((byte*)ptr + UserMsg.Size, len);
                            }
                        }
                        else
                        {
                            if (len > 0)
                            {
                                buf = new byte[len];
                                Marshal.Copy((IntPtr)(ptr + UserMsg.Size), buf, 0, len);
                            }
                        }
                    }
                    else if (msg.AllPart > 1 & msg.AllPart <= 32)
                    {
                        if (len > 0)
                        {
                            buf = new byte[len];
                            Marshal.Copy((IntPtr)(ptr + UserMsg.Size), buf, 0, len);
                        }
                    }
                }
                if (msg.DataType == EnvelopeType.String | msg.DataType == EnvelopeType.Json)
                {
                    HandleMsg(ref msg, str, null);
                }
                else
                {
                    HandleMsg(ref msg, null, buf);
                }
            }
            catch(Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message + '\n' + ex.StackTrace);
            }
        }
        public virtual void HandleMsg(ref UserMsg msg, string str, byte[] dat)
        {
            if (msg.DataType == EnvelopeType.Mate)
            {
                if (msg.Cmd == 0xffff & msg.CmdType == 255)
                {
                    Connect();
                }
            }
        }
        public byte[] EncodingData(byte dataType, UInt32 cmd, byte[] dat, byte cmdType = 0, UInt16 error = 0)
        {
            MsgID++;
            int mid = MsgID;
            int c = 0;
            if (dat != null)
                c = dat.Length;
            int len = c + UserMsg.Size;
            byte[] buf = new byte[len];
            unsafe
            {
                fixed (byte* bp = &buf[0])
                {
                    UserMsg* ip = (UserMsg*)bp;
                    ip->DataType = dataType;
                    ip->CmdType = cmdType;
                    ip->Cmd = cmd;
                    ip->Error = error;
                    ip->CurPart = 0;
                    ip->AllPart = 1;
                    ip->MsgID = mid;
                }
            }
            int s = UserMsg.Size;
            for (int i = 0; i < c; i++)
            {
                buf[s] = dat[i];
                s++;
            }
            return buf;
        }
        public byte[] EncodingNull(byte dataType, UInt32 cmd, byte cmdType, UInt16 error)
        {
            byte[] buf = new byte[UserMsg.Size];
            unsafe
            {
                fixed (byte* bp = &buf[0])
                {
                    UserMsg* ip = (UserMsg*)bp;
                    ip->DataType = dataType;
                    ip->CmdType = cmdType;
                    ip->Cmd = cmd;
                    ip->Error = error;
                    ip->AllPart = 1;
                    ip->CurPart = 0;
                    ip->MsgID = 0;
                }
            }
            return buf;
        }
        public virtual void Send(byte dataType, UInt32 cmd, byte[] dat, byte cmdType = 0, UInt16 error = 0)
        {
            if (dat == null)
            {
                Post(EncodingNull(dataType, cmd, cmdType, error));
            }
            else
            {
                Post(EncodingData(dataType, cmd, dat, cmdType, error));
            }
        }
        public virtual void SendString(UInt32 cmd, string str, byte cmdType = 0, UInt16 error = 0)
        {
            if (str == null || str == "")
            {
                Post(EncodingNull(EnvelopeType.String, cmd, cmdType, error));
            }
            else
            {
                Post(EncodingData(EnvelopeType.String, cmd, Encoding.UTF8.GetBytes(str), cmdType, error));
            }
        }
        public virtual void SendJson(UInt32 cmd, string str, byte cmdType = 0, UInt16 error = 0)
        {
            if (str == null || str == "")
            {
                Post(EncodingNull(EnvelopeType.Json, cmd, cmdType, error));
            }
            else
            {
                Post(EncodingData(EnvelopeType.Json, cmd, Encoding.UTF8.GetBytes(str), cmdType, error));
            }
        }
        public virtual bool Post(byte[] dat, int bid = 0)
        {
            NetworkContent nc = new NetworkContent();
            nc.Bid = bid;
            nc.dat = dat;
            return ncStack.Enqueue(nc);
        }
    }
}
